//
//  FileManager+Extension.swift
//  GreenLight
//
//  Created by ZWJY on 2022/8/24.
//

import Foundation

/**
 1， Home 程序目录（包含所哟资源文件和可执行文件）
 2，Documents ( - 保存应用运行时生成的需要持久化的数据，iTunes同步设备时会备份该目录)
 3，Library/Cache （保存应用运行时生成的需要持久化的数据，iTunes同步设备时不备份该目录。一般存放体积大、不需要备份的非重要数据，缓存的图片，离线数据， 由程序员管理）
 4，Library/Preference（保存应用的所有偏好设置，IOS的Settings应用会在该目录中查找应用的设置信息）
 5，tmp(保存临时文件， 重新启动手机, tmp 目录会被清空，系统磁盘空间不足时，系统也会自动清理)
 */
public extension FileManager {
    /// 移动或者拷贝的类型
    enum MoveOrCopyType {
        case file
        case directory
    }

    /// 获取home 完整的路径名
    class var homeDirectory: String {
        return NSHomeDirectory()
    }

    /// 获取document路径
    class var documnetsDirectory: String {
        let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, .userDomainMask, true)
        return path.first ?? ""
    }

    /// 获取Library路径
    class var libraryDirectory: String {
        let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, .userDomainMask, true)
        return path.first ?? ""
    }

    /// 获取/Library/Caches路径
    class var cachesDirectory: String {
        let path = homeDirectory + "/Library/Caches"
        return path
    }

    /// 获取/Library/Preferences路径
    class var preferencesDirectory: String {
        let path = homeDirectory + "/Library/Preferences"
        return path
    }

    /// 获取Tmp路径
    class var tmpDirectory: String {
        let path = homeDirectory + "/tmp"
        return path
    }
}

public extension FileManager {
    /// 创建文件夹(蓝色的，文件夹和文件是不一样的)
    /// - Parameter folderName: 文件夹的名字
    /// - Returns: 返回创建的 创建文件夹路径
    class func createFolder(folderPath: String) -> (isSuccess: Bool, error: String) {
        if judgeFileOrFolderExists(filePath: folderPath) {
            return (true, "")
        }
        // 不存在的路径才会创建
        do {
            // withIntermediateDirectories为ture表示路径中间如果有不存在的文件夹都会创建
            try FileManager.default.createDirectory(atPath: folderPath, withIntermediateDirectories: true, attributes: nil)
            return (true, "")
        } catch _ {
            return (false, "创建失败")
        }
    }

    /// 删除文件夹
    /// - Parameter folderPath: 文件的路径
    class func removefolder(folderPath: String) -> (isSuccess: Bool, error: String) {
        let filePath = "\(folderPath)"
        guard judgeFileOrFolderExists(filePath: filePath) else {
            // 不存在就不做什么操作了
            return (true, "")
        }
        // 文件存在进行删除
        do {
            try FileManager.default.removeItem(atPath: filePath)
            return (true, "")
        } catch _ {
            return (false, "删除失败")
        }
    }

    /// 创建文件
    /// - Parameter filePath: 文件路径
    /// - Returns: 返回创建的结果 和 路径
    class func createFile(filePath: String) -> (isSuccess: Bool, error: String) {
        guard judgeFileOrFolderExists(filePath: filePath) else {
            // 不存在的文件路径才会创建
            // withIntermediateDirectories 为 ture 表示路径中间如果有不存在的文件夹都会创建
            let createSuccess = FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil)
            return (createSuccess, "")
        }
        return (true, "")
    }

    /// 删除文件
    /// - Parameter filePath: 文件路径
    class func removefile(filePath: String) -> (isSuccess: Bool, error: String) {
        guard judgeFileOrFolderExists(filePath: filePath) else {
            // 不存在的文件路径就不需要要移除
            return (true, "")
        }
        // 移除文件
        do {
            try FileManager.default.removeItem(atPath: filePath)
            return (true, "")
        } catch _ {
            return (false, "移除文件失败")
        }
    }

    /**
     几个小注意点：
     1、目标路径，要带上文件夹名称，而不能只写父路径
     2、如果是覆盖拷贝，就是说目标路径已存在此文件夹，我们必须先删除，否则提示make directory error（当然这里最好做一个容错处理，比如拷贝前先转移到其他路径，如果失败，再拿回来）
     */
    /// 拷贝(文件夹/文件)的内容 到另外一个(文件夹/文件)，新的(文件夹/文件)如果存在就先删除再 拷贝
    /// - Parameters:
    ///   - fromeFile: 拷贝的(文件夹/文件)路径
    ///   - toFile: 拷贝后的(文件夹/文件)路径
    ///   - isOverwrite: 当要拷贝到的(文件夹/文件)路径存在，会拷贝失败，这里传入是否覆盖
    /// - Returns: 拷贝的结果
    class func copyFile(type: MoveOrCopyType, fromeFilePath: String, toFilePath: String, isOverwrite: Bool = true) -> (isSuccess: Bool, error: String) {
        // 1、先判断被拷贝路径是否存在
        guard judgeFileOrFolderExists(filePath: fromeFilePath) else {
            return (false, "被拷贝的(文件夹/文件)路径不存在")
        }
        // 2、判断拷贝后的文件路径的前一个文件夹路径是否存在，不存在就进行创建
        let toFileFolderPath = directoryAtPath(path: toFilePath)
        if !judgeFileOrFolderExists(filePath: toFileFolderPath), type == .file ? !createFile(filePath: toFilePath).isSuccess : !createFolder(folderPath: toFileFolderPath).isSuccess {
            return (false, "拷贝后路径前一个文件夹不存在")
        }
        // 3、如果被拷贝的(文件夹/文件)已存在，先删除，否则拷贝不了
        if isOverwrite, judgeFileOrFolderExists(filePath: toFilePath) {
            do {
                try FileManager.default.removeItem(atPath: toFilePath)
            } catch _ {
                return (false, "拷贝失败")
            }
        }
        // 4、拷贝(文件夹/文件)
        do {
            try FileManager.default.copyItem(atPath: fromeFilePath, toPath: toFilePath)
        } catch _ {
            return (false, "拷贝失败")
        }
        return (true, "success")
    }

    /// 移动(文件夹/文件)的内容 到另外一个(文件夹/文件)，新的(文件夹/文件)如果存在就先删除再 移动
    /// - Parameters:
    ///   - fromeFile: 被移动的文件路径
    ///   - toFile: 移动后的文件路径
    @discardableResult
    static func moveFile(type: MoveOrCopyType, fromeFilePath: String, toFilePath: String, isOverwrite: Bool = true) -> (isSuccess: Bool, error: String) {
        // 1、先判断被拷贝路径是否存在
        guard judgeFileOrFolderExists(filePath: fromeFilePath) else {
            return (false, "被移动的(文件夹/文件)路径不存在")
        }
        // 2、判断拷贝后的文件路径的前一个文件夹路径是否存在，不存在就进行创建
        let toFileFolderPath = directoryAtPath(path: toFilePath)
        if !judgeFileOrFolderExists(filePath: toFileFolderPath), type == .file ? !createFile(filePath: toFilePath).isSuccess : !createFolder(folderPath: toFileFolderPath).isSuccess {
            return (false, "移动后路径前一个文件夹不存在")
        }
        // 3、如果被移动的(文件夹/文件)已存在，先删除，否则拷贝不了
        if isOverwrite, judgeFileOrFolderExists(filePath: toFilePath) {
            do {
                try FileManager.default.removeItem(atPath: toFilePath)
            } catch _ {
                return (false, "移动失败")
            }
        }
        // 4、移动(文件夹/文件)
        do {
            try FileManager.default.moveItem(atPath: fromeFilePath, toPath: toFilePath)
        } catch _ {
            return (false, "移动失败")
        }
        return (true, "success")
    }

    /// 对指定路径执行浅搜索，返回指定目录路径下的文件、子目录及符号链接的列表(只寻找一层)
    /// - Parameter folderPath: 建搜索的lujing
    /// - Returns: 指定目录路径下的文件、子目录及符号链接的列表
    static func shallowSearchAllFiles(folderPath: String) -> Array<String>? {
        guard let contentsOfDirectoryArray = try? FileManager.default.contentsOfDirectory(atPath: folderPath) else {
            return nil
        }
        return contentsOfDirectoryArray
    }

    /**深度遍历，会递归遍历子文件夹（包括符号链接，所以要求性能的话用enumeratorAtPath）*/
    static func getAllFileNames(folderPath: String) -> Array<String>? {
        // 查看文件夹是否存在，如果存在就直接读取，不存在就直接反空
        guard judgeFileOrFolderExists(filePath: folderPath), let subPaths = FileManager.default.subpaths(atPath: folderPath) else {
            return nil
        }
        return subPaths
    }

    // MARK: 2.20、深度遍历，会递归遍历子文件夹（但不会递归符号链接）
    /** 对指定路径深度遍历，会递归遍历子文件夹（但不会递归符号链接））*/
    static func deepSearchAllFiles(folderPath: String) -> Array<Any>? {
        // 查看文件夹是否存在，如果存在就直接读取，不存在就直接反空
        guard judgeFileOrFolderExists(filePath: folderPath), let contentsOfPathArray = FileManager.default.enumerator(atPath: folderPath) else {
            return nil
        }
        return contentsOfPathArray.allObjects
    }


    /// 判断文件或文件夹是否存在
    /// 查看文件夹是否存在，如果存在就直接读取，不存在就直接反空
    class func judgeFileOrFolderExists(filePath: String) -> Bool {
        let exist = FileManager.default.fileExists(atPath: filePath)
        return exist
    }

    /// 获取 (文件夹/文件) 的前一个路径
    class func directoryAtPath(path: String) -> String {
        return (path as NSString).deletingLastPathComponent
    }

    /// 判断目录是否可读
    class func judegeIsReadableFile(path: String) -> Bool {
        return  FileManager.default.isReadableFile(atPath: path)
    }

    /// 判断目录是否可写
    class func judegeIsWritableFile(path: String) -> Bool {
        return  FileManager.default.isReadableFile(atPath: path)
    }

    /// 判断目录是否可执行
    class func judegeIsExecutableFile(path: String) -> Bool {
        return  FileManager.default.isExecutableFile(atPath: path)
    }

    /// 判断目录是否可删除
    class func judegeIsDeletableFile(path: String) -> Bool {
        return  FileManager.default.isDeletableFile(atPath: path)
    }

    /// 根据文件路径获取文件扩展类型(.ppt)
    class func fileSuffixAtPath(path: String) -> String {
        return (path as NSString).pathExtension
    }

    /// 根据文件路径获取文件名称，是否需要后缀
    class func fileName(path: String, suffix: Bool = true) -> String {
        let fileName = (path as NSString).lastPathComponent
        guard suffix else {
            // 删除后缀
            return (fileName as NSString).deletingPathExtension
        }
        return fileName
    }

    /// 计算单个 (文件夹/文件) 的大小，单位为字节 （没有进行转换的）
    class func fileOrDirectorySingleSize(filePath: String) -> UInt64 {
        // 1、先判断文件路径是否存在
        guard judgeFileOrFolderExists(filePath: filePath) else {
            return 0
        }
        // 2、读取文件大小
        guard let fileAttributes = try? FileManager.default.attributesOfItem(atPath: filePath), let fileSizeValue = fileAttributes[FileAttributeKey.size] as? UInt64 else {
            return 0
        }
        return fileSizeValue
    }
}
